#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the version 3 of the GNU Lesser General Public License
#   as published by the Free Software Foundation.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Copyright (c) NEC Deutschland GmbH, NEC HPC Europe
#
# $Id$

import logging
import socket
import xml.sax.handler

from aggmon.importer import Importer, ImporterComponent
from basic_types.metric import Metric
from aggmon.metric_containers.ganglia_xml.ganglia_xml_metriccontainer import GangliaXMLMetricContainer
from basic_types.helpers import is_ip_address


__all__ = ["GangliaXMLMetricImporter"]


class GangliaXMLImporter(Importer, xml.sax.handler.ContentHandler):
    def __init__( self, host_name="localhost", only_self=True, only_group=False, port=8649, **kwds ):
        logging.debug( "GangliaXMLImporter: host_name=%s only_self=%s only_group=%s port=%d kwds=%s" % (host_name, str(only_self), str(only_group), port, repr(kwds))  )
        Importer.__init__( self, **kwds )
        xml.sax.handler.ContentHandler.__init__( self )
        self._connect_address = ( host_name, port )
        self.metriccontainer = GangliaXMLMetricContainer()
        self._only = only_self
        self._only_group = only_group
        if only_group:
            self._only = False
        self._my_host_name = host_name

    def _parseGangliaXML( self, ganglia_xml ):
        self.requests = []
        xml.sax.parseString( ganglia_xml, self )
        for request in self.requests:
            yield request

    def poll( self ):                    
        s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
        s.connect( self._connect_address )
        
        buffers = []
        while True:
            try:
                buffer = s.recv( 1024 )
                if len( buffer ) > 0:
                    buffers.append( buffer )
                else:
                    break
            except IOError:
                break

        if len( buffers ) > 0:            
            for request in self._parseGangliaXML( "".join( buffers ) ):
                yield request        
            

class GangliaXMLMetricImporter(GangliaXMLImporter):
    def startElement( self, name, attrs ):
        if name == "HOST":
            host = attrs.getValue( "NAME" )
            if not is_ip_address( host ):
                self._host_name = attrs.getValue( "NAME" ).split(".")[0]
            else:
                self._host_name = attrs.getValue( "NAME" )
            #print "ganglia xml: HOST", self._host_name
            self._host_reported_s = long( attrs.getValue( "REPORTED" ) )
            
            # add the ganglia heartbeat metric
            # TODO: are we sure we want this stored in the database time-series?
            if (self._only and self._host_name == self._my_host_name) or \
                (self._only_group and self.getHierarchy().in_own_groups( self._host_name )) or \
                (not self._only and not self._only_group):
                tn=long( attrs.getValue( "TN" ) )
                time=long(self._localtime_s) - tn
                #print "Metric: name=%s, host=%s, value=%s, source=%s, time=%s" %(attrs.getValue( "NAME" ), self._host_name, str(_value), attrs.getValue( "SOURCE" ), str(time))
                metric = Metric( name="heartbeat",
                                 host=self._host_name,
                                 value = self._host_reported_s,
                                 #tn=tn,
                                 no_log=1,      # metric won't go into time series
                                 time=time,
                                 tmax=int( attrs.getValue( "TMAX" ) ),
                                 source="gmond" )
                if self.metriccontainer.isNew( metric ):
                    self.requests.append( metric )
            

        elif name == "CLUSTER":
            self._localtime_s = long( attrs.getValue( "LOCALTIME" ) )

        elif name == "METRIC":
            #print "ganglia xml: METRIC", attrs.getValue( "NAME" )
            try:
                _value = float( attrs.getValue( "VAL" ) )
            except ValueError:
                return                

            if (self._only and self._host_name == self._my_host_name) or \
                (self._only_group and self.getHierarchy().in_own_groups( self._host_name )) or \
                (not self._only and not self._only_group):
                tn=long( attrs.getValue( "TN" ) )
                time=long(self._localtime_s) - tn
                #print "Metric: name=%s, host=%s, value=%s, source=%s, time=%s" %(attrs.getValue( "NAME" ), self._host_name, str(_value), attrs.getValue( "SOURCE" ), str(time))
                metric = Metric( name=attrs.getValue( "NAME" ),
                                 host=self._host_name,
                                 value = _value,
                                 tn=tn,
                                 time=time,
                                 type_=attrs.getValue( "TYPE" ),
                                 units=attrs.getValue( "UNITS" ),                             
                                 tmax=int( attrs.getValue( "TMAX" ) ),
                                 dmax=int( attrs.getValue( "DMAX" ) ),                             
                                 source=attrs.getValue( "SOURCE" ) )
                if self.metriccontainer.isNew( metric ):
                    self.requests.append( metric )


class GangliaXMLMetricImporterComponent(ImporterComponent):
    def __init__( self, *args, **kwds ):
        ImporterComponent.__init__( self, *args, **kwds)
        self.allowed_kwds.extend( ["host_name", "only_self", "only_group", "port"] )

    def instantiate( self, *args, **kwds ):
        self.instance = None
        try:
            logging.debug( "instantiating: %s" % self.name )
            self.instance = GangliaXMLMetricImporter( *args, **kwds )
        except Exception, e:
            raise e
